home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / imap / ANSI / c-client / mail.c < prev    next >
C/C++ Source or Header  |  1996-10-15  |  54KB  |  1,760 lines

  1. /*
  2.  * Program:    Mailbox Access routines
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    22 November 1989
  13.  * Last Edited:    15 October 1996
  14.  *
  15.  * Copyright 1996 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36.  
  37. #include <ctype.h>
  38. #include <stdio.h>
  39. #include "mail.h"
  40. #include "osdep.h"
  41. #include <time.h>
  42. #include "misc.h"
  43. #include "rfc822.h"
  44.  
  45.  
  46. /* c-client global data */
  47.  
  48.                 /* list of mail drivers */
  49. static DRIVER *maildrivers = NIL;
  50.                 /* pointer to alternate gets function */
  51. static mailgets_t mailgets = NIL;
  52.                 /* mail cache manipulation function */
  53. static mailcache_t mailcache = mm_cache;
  54.                 /* place to stash last user name */
  55. static char *mailusernamebuf = NIL;
  56.                 /* pointer to alternate rfc822 generator */
  57. static rfc822emit_t rfc822_emit = rfc822_output;
  58.                 /* pointer to SMTP verbose output collector */
  59. static postverbose_t post_verbose = NIL;
  60.                 /* POST send string as record */
  61. static postsoutr_t post_soutr = tcp_soutr;
  62.                 /* POST receive line */
  63. static postgetline_t post_getline = tcp_getline;
  64.                 /* POST close connection */
  65. static postclose_t post_close = tcp_close;
  66.  
  67. /* Default limited get string
  68.  * Accepts: readin function pointer
  69.  *        stream to use
  70.  *        number of bytes
  71.  * Returns: string read in, truncated if necessary
  72.  *
  73.  * This is a sample mailgets routine.  It simply truncates any data larger
  74.  * than MAXMESSAGESIZE.  On most systems, you generally don't use a mailgets
  75.  * routine at all, but on some systems (e.g. DOS) it's required to prevent the
  76.  * application from crashing.  This one is filled in by the os driver for any
  77.  * OS that requires a mailgets routine and the main program has not already
  78.  * supplied one, generally in tcp_open().
  79.  */
  80.  
  81. char *mm_gets (readfn_t f,void *stream,unsigned long size)
  82. {
  83.   char *s;
  84.   char tmp[MAILTMPLEN+1];
  85.   unsigned long i,j = 0;
  86.                 /* truncate? */
  87.   if (i = (size > MAXMESSAGESIZE)) {
  88.     sprintf (tmp,"%ld character literal truncated to %ld characters",
  89.          size,MAXMESSAGESIZE);
  90.     mm_log (tmp,WARN);        /* warn user */
  91.     i = size - MAXMESSAGESIZE;    /* number of bytes of slop */
  92.     size = MAXMESSAGESIZE;    /* maximum length string we can read */
  93.   }
  94.   s = (char *) fs_get (size + 1);
  95.   *s = s[size] = '\0';        /* init in case getbuffer fails */
  96.   (*f) (stream,size,s);        /* get the literal */
  97.                 /* toss out everything after that */
  98.   while (i -= j) (*f) (stream,j = min ((long) MAILTMPLEN,i),tmp);
  99.   return s;
  100. }
  101.  
  102. /* Default mail cache handler
  103.  * Accepts: pointer to cache handle
  104.  *        message number
  105.  *        caching function
  106.  * Returns: cache data
  107.  */
  108.  
  109. void *mm_cache (MAILSTREAM *stream,long msgno,long op)
  110. {
  111.   size_t new;
  112.   void *ret = NIL;
  113.   long i = msgno - 1;
  114.   unsigned long j = stream->cachesize;
  115.   switch ((int) op) {        /* what function? */
  116.   case CH_INIT:            /* initialize cache */
  117.     if (stream->cachesize) {    /* flush old cache contents */
  118.       while (stream->cachesize) mm_cache (stream,stream->cachesize--,CH_FREE);
  119.       fs_give ((void **) &stream->cache.c);
  120.       stream->nmsgs = 0;    /* can't have any messages now */
  121.     }
  122.     break;
  123.   case CH_SIZE:            /* (re-)size the cache */
  124.     if (msgno > j) {        /* do nothing if size adequate */
  125.       new = (stream->cachesize = msgno + CACHEINCREMENT) * sizeof (void *);
  126.       if (stream->cache.c) fs_resize ((void **) &stream->cache.c,new);
  127.       else stream->cache.c = (void **) fs_get (new);
  128.                 /* init cache */
  129.       while (j < stream->cachesize) stream->cache.c[j++] = NIL;
  130.     }
  131.     break;
  132.   case CH_MAKELELT:        /* return long elt, make if necessary */
  133.     if (!stream->cache.c[i]) {    /* have one already? */
  134.                 /* no, instantiate it */
  135.       stream->cache.l[i] = (LONGCACHE *) fs_get (sizeof (LONGCACHE));
  136.       memset (&stream->cache.l[i]->elt,0,sizeof (MESSAGECACHE));
  137.       stream->cache.l[i]->elt.lockcount = 1;
  138.       stream->cache.l[i]->elt.msgno = msgno;
  139.       stream->cache.l[i]->env = NIL;
  140.       stream->cache.l[i]->body = NIL;
  141.     }
  142.                 /* drop in to CH_LELT */
  143.   case CH_LELT:            /* return long elt */
  144.     ret = stream->cache.c[i];    /* return void version */
  145.     break;
  146.  
  147.   case CH_MAKEELT:        /* return short elt, make if necessary */
  148.     if (!stream->cache.c[i]) {    /* have one already? */
  149.       if (stream->scache) {    /* short cache? */
  150.     stream->cache.s[i] = (MESSAGECACHE *) fs_get (sizeof(MESSAGECACHE));
  151.     memset (stream->cache.s[i],0,sizeof (MESSAGECACHE));
  152.     stream->cache.s[i]->lockcount = 1;
  153.     stream->cache.s[i]->msgno = msgno;
  154.       }
  155.       else mm_cache (stream,msgno,CH_MAKELELT);
  156.     }
  157.                 /* drop in to CH_ELT */
  158.   case CH_ELT:            /* return short elt */
  159.     ret = stream->cache.c[i] && !stream->scache ?
  160.       (void *) &stream->cache.l[i]->elt : stream->cache.c[i];
  161.     break;
  162.   case CH_FREE:            /* free (l)elt */
  163.     if (stream->scache) mail_free_elt (&stream->cache.s[i]);
  164.     else mail_free_lelt (&stream->cache.l[i]);
  165.     break;
  166.   case CH_EXPUNGE:        /* expunge cache slot */
  167.                 /* slide down remainder of cache */
  168.     for (i = msgno; i < stream->nmsgs; ++i)
  169.       if (stream->cache.c[i-1] = stream->cache.c[i])
  170.     ((MESSAGECACHE *) mm_cache (stream,i,CH_ELT))->msgno = i;
  171.     stream->cache.c[stream->nmsgs-1] = NIL;
  172.     break;
  173.   default:
  174.     fatal ("Bad mm_cache op");
  175.     break;
  176.   }
  177.   return ret;
  178. }
  179.  
  180. /* Dummy string driver for complete in-memory strings */
  181.  
  182. STRINGDRIVER mail_string = {
  183.   mail_string_init,        /* initialize string structure */
  184.   mail_string_next,        /* get next byte in string structure */
  185.   mail_string_setpos        /* set position in string structure */
  186. };
  187.  
  188.  
  189. /* Initialize mail string structure for in-memory string
  190.  * Accepts: string structure
  191.  *        pointer to string
  192.  *        size of string
  193.  */
  194.  
  195. void mail_string_init (STRING *s,void *data,unsigned long size)
  196. {
  197.                 /* set initial string pointers */
  198.   s->chunk = s->curpos = (char *) (s->data = data);
  199.                 /* and sizes */
  200.   s->size = s->chunksize = s->cursize = size;
  201.   s->data1 = s->offset = 0;    /* never any offset */
  202. }
  203.  
  204. /* Get next character from string
  205.  * Accepts: string structure
  206.  * Returns: character, string structure chunk refreshed
  207.  */
  208.  
  209. char mail_string_next (STRING *s)
  210. {
  211.   return *s->curpos++;        /* return the last byte */
  212. }
  213.  
  214.  
  215. /* Set string pointer position
  216.  * Accepts: string structure
  217.  *        new position
  218.  */
  219.  
  220. void mail_string_setpos (STRING *s,unsigned long i)
  221. {
  222.   s->curpos = s->chunk + i;    /* set new position */
  223.   s->cursize = s->chunksize - i;/* and new size */
  224. }
  225.  
  226. /* Mail routines
  227.  *
  228.  *  mail_xxx routines are the interface between this module and the outside
  229.  * world.  Only these routines should be referenced by external callers.
  230.  *
  231.  *  Note that there is an important difference between a "sequence" and a
  232.  * "message #" (msgno).  A sequence is a string representing a sequence in
  233.  * {"n", "n:m", or combination separated by commas} format, whereas a msgno
  234.  * is a single integer.
  235.  *
  236.  */
  237.  
  238. /* Mail link driver
  239.  * Accepts: driver to add to list
  240.  */
  241.  
  242. void mail_link (DRIVER *driver)
  243. {
  244.   DRIVER **d = &maildrivers;
  245.   while (*d) d = &(*d)->next;    /* find end of list of drivers */
  246.   *d = driver;            /* put driver at the end */
  247.   driver->next = NIL;        /* this driver is the end of the list */
  248. }
  249.  
  250. /* Mail manipulate driver parameters
  251.  * Accepts: mail stream
  252.  *        function code
  253.  *        function-dependent value
  254.  * Returns: function-dependent return value
  255.  */
  256.  
  257. void *mail_parameters (MAILSTREAM *stream,long function,void *value)
  258. {
  259.   void *r,*ret = NIL;
  260.   DRIVER *d;
  261.   switch ((int) function) {
  262.   case SET_DRIVERS:
  263.     fatal ("SET_DRIVERS not permitted");
  264.   case GET_DRIVERS:
  265.     ret = (void *) maildrivers;
  266.     break;
  267.   case SET_GETS:
  268.     mailgets = (mailgets_t) value;
  269.   case GET_GETS:
  270.     ret = (void *) mailgets;
  271.     break;
  272.   case SET_CACHE:
  273.     mailcache = (mailcache_t) value;
  274.   case GET_CACHE:
  275.     ret = (void *) mailcache;
  276.     break;
  277.   case SET_USERNAMEBUF:
  278.     mailusernamebuf = (char *) value;
  279.     /* BUG: missing break?  anyone care? */
  280.   case GET_USERNAMEBUF:
  281.     ret = (void *) mailusernamebuf;
  282.     break;
  283.   case SET_RFC822OUTPUT:
  284.     rfc822_emit = (rfc822emit_t) value;
  285.     break;
  286.   case GET_RFC822OUTPUT:
  287.     ret = (void *) rfc822_emit;
  288.     break;
  289.   case SET_POSTVERBOSE:
  290.     post_verbose = (postverbose_t) value;
  291.     break;
  292.   case GET_POSTVERBOSE:
  293.     ret = (void *) post_verbose;
  294.     break;
  295.   case SET_POSTSOUTR:
  296.     post_soutr = (postsoutr_t) value;
  297.     break;
  298.   case GET_POSTSOUTR:
  299.     ret = (void *) post_soutr;
  300.     break;
  301.   case SET_POSTGETLINE:
  302.     post_getline = (postgetline_t) value;
  303.     break;
  304.   case GET_POSTGETLINE:
  305.     ret = (void *) post_getline;
  306.     break;
  307.   case SET_POSTCLOSE:
  308.     post_close = (postclose_t) value;
  309.     break;
  310.   case GET_POSTCLOSE:
  311.     ret = (void *) post_close;
  312.     break;
  313.   default:
  314.     if (stream && stream->dtb)    /* if have stream, do for that stream only */
  315.       ret = (*stream->dtb->parameters) (function,value);
  316.                 /* else do all drivers */
  317.     else for (d = maildrivers; d; d = d->next)
  318.       if (r = (d->parameters) (function,value)) ret = r;
  319.                 /* do environment */
  320.     if (r = env_parameters (function,value)) ret = r;
  321.                 /* do TCP/IP */
  322.     if (r = tcp_parameters (function,value)) ret = r;
  323.     break;
  324.   }
  325.   return ret;
  326. }
  327.  
  328. /* Mail find list of subscribed mailboxes
  329.  * Accepts: mail stream
  330.  *        pattern to search
  331.  */
  332.  
  333. void mail_find (MAILSTREAM *stream,char *pat)
  334. {
  335.   DRIVER *d = maildrivers;
  336.                 /* if have a stream, do it for that stream */
  337.   if (stream && stream->dtb) (*stream->dtb->find) (stream,pat);
  338.   else do (d->find) (NIL,pat);    /* otherwise do for all DTB's */
  339.   while (d = d->next);        /* until at the end */
  340. }
  341.  
  342.  
  343. /* Mail find list of subscribed bboards
  344.  * Accepts: mail stream
  345.  *        pattern to search
  346.  */
  347.  
  348. void mail_find_bboards (MAILSTREAM *stream,char *pat)
  349. {
  350.   DRIVER *d = maildrivers;
  351.   if (stream && stream->dtb) (*stream->dtb->find_bboard) (stream,pat);
  352.   else do (d->find_bboard) (NIL,pat);
  353.   while (d = d->next);        /* until at the end */
  354. }
  355.  
  356. /* Mail find list of all mailboxes
  357.  * Accepts: mail stream
  358.  *        pattern to search
  359.  */
  360.  
  361. void mail_find_all (MAILSTREAM *stream,char *pat)
  362. {
  363.   DRIVER *d = maildrivers;
  364.                 /* if have a stream, do it for that stream */
  365.   if (stream && stream->dtb) (*stream->dtb->find_all) (stream,pat);
  366.                 /* otherwise do for all DTB's */
  367.   else do (d->find_all) (NIL,pat);
  368.   while (d = d->next);        /* until at the end */
  369. }
  370.  
  371.  
  372. /* Mail find list of all bboards
  373.  * Accepts: mail stream
  374.  *        pattern to search
  375.  */
  376.  
  377. void mail_find_all_bboard (MAILSTREAM *stream,char *pat)
  378. {
  379.   DRIVER *d = maildrivers;
  380.                 /* if have a stream, do it for that stream */
  381.   if (stream && stream->dtb) (*stream->dtb->find_all_bboard) (stream,pat);
  382.                 /* otherwise do for all DTB's */
  383.   else do (d->find_all_bboard) (NIL,pat);
  384.   while (d = d->next);        /* until at the end */
  385. }
  386.  
  387. /* Mail validate mailbox name
  388.  * Accepts: MAIL stream
  389.  *        mailbox name
  390.  *        purpose string for error message
  391.  * Return: driver factory on success, NIL on failure
  392.  */
  393.  
  394. DRIVER *mail_valid (MAILSTREAM *stream,char *mailbox,char *purpose)
  395. {
  396.   char *s,tmp[MAILTMPLEN];
  397.   DRIVER *factory;
  398.   for (factory = maildrivers; factory && !(*factory->valid) (mailbox);
  399.        factory = factory->next);
  400.                 /* must match stream if not dummy */
  401.   if (factory && stream && (stream->dtb != factory))
  402.     factory = strcmp (factory->name,"dummy") ? NIL : stream->dtb;
  403.   if (!factory && purpose) {    /* if want an error message */
  404.     switch (*mailbox) {        /* error depends upon first character */
  405.     case '*':            /* bboard */
  406.       if (mailbox[1] != '{') {    /* but only if local */
  407.     s = "no such bboard";
  408.     break;
  409.       }
  410.                 /* otherwise drop into remote */
  411.     case '{':            /* remote specification */
  412.       s = "invalid remote specification";
  413.       break;
  414.     default:            /* others */
  415.       s = "no such mailbox";
  416.       break;
  417.     }
  418.     sprintf (tmp,"Can't %s %s: %s",purpose,mailbox,s);
  419.     mm_log (tmp,ERROR);
  420.   }
  421.   return factory;        /* return driver factory */
  422. }
  423.  
  424. /* Mail validate network mailbox name
  425.  * Accepts: mailbox name
  426.  *        mailbox driver to validate against
  427.  *        pointer to where to return host name if non-NIL
  428.  *        pointer to where to return mailbox name if non-NIL
  429.  * Returns: driver on success, NIL on failure
  430.  */
  431.  
  432. DRIVER *mail_valid_net (char *name,DRIVER *drv,char *host,char *mailbox)
  433. {
  434.   NETMBX mb;
  435.   if (!mail_valid_net_parse (name,&mb) || 
  436.       (strcmp (mb.service,"imap") ? strcmp (mb.service,drv->name) :
  437.        strncmp (drv->name,"imap",4))) return NIL;
  438.   if (host) strcpy (host,mb.host);
  439.   if (mailbox) strcpy (mailbox,mb.mailbox);
  440.   return drv;
  441. }
  442.  
  443.  
  444. /* Mail validate network mailbox name
  445.  * Accepts: mailbox name
  446.  *        NETMBX structure to return values
  447.  * Returns: T on success, NIL on failure
  448.  */
  449.  
  450. long mail_valid_net_parse (char *name,NETMBX *mb)
  451. {
  452.   long i;
  453.   char c,*s,*t,*v;
  454.   mb->port = 0;            /* initialize structure */
  455.   *mb->host = *mb->mailbox = *mb->service = '\0';
  456.                 /* init flags */
  457.   mb->anoflag = mb->dbgflag = NIL;
  458.   if (mailusernamebuf) *mailusernamebuf = '\0';
  459.                 /* check if bboard */
  460.   if (mb->bbdflag = (*name == '*') ? T : NIL) name++;
  461.                 /* have host specification? */
  462.   if (!(*name == '{' && (t = strchr (s = name+1,'}')) && (i = t - s)))
  463.     return NIL;            /* not valid host specification */
  464.   strncpy (mb->host,s,i);    /* set host name */
  465.   mb->host[i] = '\0';        /* tie it off */
  466.   strcpy (mb->mailbox,t+1);    /* set mailbox name */
  467.                 /* any switches or port specification? */
  468.   if (t = strpbrk (mb->host,"/:")) {
  469.     c = *t;            /* yes, remember delimiter */
  470.     *t++ = '\0';        /* tie off host name */
  471.     lcase (t);            /* coerce remaining stuff to lowercase */
  472.     do switch (c) {        /* act based upon the character */
  473.     case ':':            /* port specification */
  474.       if (mb->port || ((mb->port = strtol (t,&t,10)) <= 0)) return NIL;
  475.       c = t ? *t++ : '\0';    /* get delimiter, advance pointer */
  476.       break;
  477.  
  478.     case '/':            /* switch */
  479.                 /* find delimiter */
  480.       if (t = strpbrk (s = t,"/:=")) {
  481.     c = *t;            /* remember delimiter for later */
  482.     *t++ = '\0';        /* tie off switch name */
  483.       }
  484.       else c = '\0';        /* no delimiter */
  485.       if (c == '=') {        /* parse switches which take arguments */
  486.     if (t = strpbrk (v = t,"/:")) {
  487.       c = *t;        /* remember delimiter for later */
  488.       *t++ = '\0';        /* tie off switch name */
  489.     }
  490.     else c = '\0';        /* no delimiter */
  491.     if (!strcmp (s,"service")) {
  492.       if (*mb->service) return NIL;
  493.       else strcpy (mb->service,v);
  494.     }
  495.     if (!strcmp (s,"user") && mailusernamebuf) {
  496.       if (*mailusernamebuf) return NIL;
  497.       else strcpy (mailusernamebuf,v);
  498.     }
  499.     else return NIL;    /* invalid argument switch */
  500.       }
  501.       else {            /* non-argument switch */
  502.     if (!strcmp (s,"anonymous")) mb->anoflag = T;
  503.     else if (!strcmp (s,"bboard")) mb->bbdflag = T;
  504.     else if (!strcmp (s,"debug")) mb->dbgflag = T;
  505.     else if (!strcmp (s,"imap") || !strcmp (s,"imap2") ||
  506.          !strcmp (s,"imap4") || !strcmp (s,"pop3") ||
  507.          !strcmp (s,"nntp")) {
  508.       if (*mb->service) return NIL;
  509.       else strcpy (mb->service,s);
  510.     } 
  511.     else return NIL;    /* invalid non-argument switch */
  512.       }
  513.       break;
  514.     default:            /* anything else is bogus */
  515.       return NIL;
  516.     } while (c);        /* see if anything more to parse */
  517.   }
  518.                 /* default mailbox name */
  519.   if (!*mb->mailbox) strcpy (mb->mailbox,mb->bbdflag ? "general" : "INBOX");
  520.                 /* default service name */
  521.   if (!*mb->service) strcpy (mb->service,"imap");
  522.   return T;
  523. }
  524.  
  525. /* Mail subscribe to mailbox
  526.  * Accepts: mail stream
  527.  *        mailbox to add to subscription list
  528.  * Returns: T on success, NIL on failure
  529.  */
  530.  
  531. long mail_subscribe (MAILSTREAM *stream,char *mailbox)
  532. {
  533.   DRIVER *factory = mail_valid (stream,mailbox,"subscribe to mailbox");
  534.   return factory ? (*factory->subscribe) (stream,mailbox) : NIL;
  535. }
  536.  
  537.  
  538. /* Mail unsubscribe to mailbox
  539.  * Accepts: mail stream
  540.  *        mailbox to delete from subscription list
  541.  * Returns: T on success, NIL on failure
  542.  */
  543.  
  544. long mail_unsubscribe (MAILSTREAM *stream,char *mailbox)
  545. {
  546.  DRIVER *factory = mail_valid (stream,mailbox,"unsubscribe to mailbox");
  547.   return factory ? (*factory->unsubscribe) (stream,mailbox) : NIL;
  548. }
  549.  
  550.  
  551. /* Mail subscribe to bboard
  552.  * Accepts: mail stream
  553.  *        bboard to add to subscription list
  554.  * Returns: T on success, NIL on failure
  555.  */
  556.  
  557. long mail_subscribe_bboard (MAILSTREAM *stream,char *mailbox)
  558. {
  559.   char tmp[MAILTMPLEN];
  560.   DRIVER *factory;
  561.   sprintf (tmp,"*%s",mailbox);
  562.   return (factory = mail_valid (stream,tmp,"subscribe to bboard")) ?
  563.     (*factory->subscribe_bboard) (stream,mailbox) : NIL;
  564. }
  565.  
  566.  
  567. /* Mail unsubscribe to bboard
  568.  * Accepts: mail stream
  569.  *        bboard to delete from subscription list
  570.  * Returns: T on success, NIL on failure
  571.  */
  572.  
  573. long mail_unsubscribe_bboard (MAILSTREAM *stream,char *mailbox)
  574. {
  575.   char tmp[MAILTMPLEN];
  576.   DRIVER *factory;
  577.   sprintf (tmp,"*%s",mailbox);
  578.   return (factory = mail_valid (stream,tmp,"unsubscribe to bboard")) ?
  579.     (*factory->unsubscribe_bboard) (stream,mailbox) : NIL;
  580. }
  581.  
  582. /* Mail create mailbox
  583.  * Accepts: mail stream
  584.  *        mailbox name to create
  585.  * Returns: T on success, NIL on failure
  586.  */
  587.  
  588. long mail_create (MAILSTREAM *stream,char *mailbox)
  589. {
  590.   /* A local mailbox is one that is not qualified as being a remote or a
  591.    * namespace mailbox.  Any remote or namespace mailbox driver must check
  592.    * for itself whether or not the mailbox already exists. */
  593.   int localp = (*mailbox != '{') && (*mailbox != '#');
  594.   char tmp[MAILTMPLEN];
  595.                 /* guess at driver if stream not specified */
  596.   if (!(stream || (stream = localp ? default_proto () :
  597.            mail_open (NIL,mailbox,OP_PROTOTYPE)))) {
  598.     sprintf (tmp,"Can't create mailbox %s: indeterminate format",mailbox);
  599.     mm_log (tmp,ERROR);
  600.     return NIL;
  601.   }
  602.                 /* must not already exist if local */
  603.   if (localp && mail_valid (stream,mailbox,NIL)) {
  604.     sprintf (tmp,"Can't create mailbox %s: mailbox already exists",mailbox);
  605.     mm_log (tmp,ERROR);
  606.     return NIL;
  607.   }
  608.   return stream->dtb ? (*stream->dtb->create) (stream,mailbox) : NIL;
  609. }
  610.  
  611. /* Mail delete mailbox
  612.  * Accepts: mail stream
  613.  *        mailbox name to delete
  614.  * Returns: T on success, NIL on failure
  615.  */
  616.  
  617. long mail_delete (MAILSTREAM *stream,char *mailbox)
  618. {
  619.   DRIVER *factory = mail_valid (stream,mailbox,"delete mailbox");
  620.   return factory ? (*factory->mbxdel) (stream,mailbox) : NIL;
  621. }
  622.  
  623.  
  624. /* Mail rename mailbox
  625.  * Accepts: mail stream
  626.  *        old mailbox name
  627.  *        new mailbox name
  628.  * Returns: T on success, NIL on failure
  629.  */
  630.  
  631. long mail_rename (MAILSTREAM *stream,char *old,char *new)
  632. {
  633.   char tmp[MAILTMPLEN];
  634.   DRIVER *factory = mail_valid (stream,old,"rename mailbox");
  635.   if ((*old != '{') && (*old != '#') && mail_valid (NIL,new,NIL)) {
  636.     sprintf (tmp,"Can't rename to mailbox %s: mailbox already exists",new);
  637.     mm_log (tmp,ERROR);
  638.     return NIL;
  639.   }
  640.   return factory ? (*factory->mbxren) (stream,old,new) : NIL;
  641. }
  642.  
  643. /* Mail open
  644.  * Accepts: candidate stream for recycling
  645.  *        mailbox name
  646.  *        open options
  647.  * Returns: stream to use on success, NIL on failure
  648.  */
  649.  
  650. MAILSTREAM *mail_open (MAILSTREAM *stream,char *name,long options)
  651. {
  652.   DRIVER *factory = mail_valid (NIL,name,(options & OP_SILENT) ?
  653.                 NIL : "open mailbox");
  654.   if (factory) {        /* must have a factory */
  655.     if (!stream) {        /* instantiate stream if none to recycle */
  656.       if (options & OP_PROTOTYPE) return (*factory->open) (NIL);
  657.       stream = (MAILSTREAM *) fs_get (sizeof (MAILSTREAM));
  658.                 /* initialize stream */
  659.       memset ((void *) stream,0,sizeof (MAILSTREAM));
  660.       stream->dtb = factory;    /* set dispatch */
  661.                 /* set mailbox name */
  662.       stream->mailbox = cpystr (name);
  663.                 /* initialize cache */
  664.       (*mailcache) (stream,(long) 0,CH_INIT);
  665.     }
  666.     else {            /* close driver if different from factory */
  667.       if (stream->dtb != factory) {
  668.     if (stream->dtb) (*stream->dtb->close) (stream);
  669.     stream->dtb = factory;    /* establish factory as our driver */
  670.     stream->local = NIL;    /* flush old driver's local data */
  671.     mail_free_cache (stream);
  672.       }
  673.                 /* clean up old mailbox name for recycling */
  674.       if (stream->mailbox) fs_give ((void **) &stream->mailbox);
  675.       stream->mailbox = cpystr (name);
  676.     }
  677.     stream->lock = NIL;        /* initialize lock and options */
  678.     stream->debug = (options & OP_DEBUG) ? T : NIL;
  679.     stream->rdonly = (options & OP_READONLY) ? T : NIL;
  680.     stream->anonymous = (options & OP_ANONYMOUS) ? T : NIL;
  681.     stream->scache = (options & OP_SHORTCACHE) ? T : NIL;
  682.     stream->silent = (options & OP_SILENT) ? T : NIL;
  683.     stream->halfopen = (options & OP_HALFOPEN) ? T : NIL;
  684.                 /* have driver open, flush if failed */
  685.     if (!(*factory->open) (stream)) stream = mail_close (stream);
  686.   }
  687.   return stream;        /* return the stream */
  688. }
  689.  
  690. /* Mail close
  691.  * Accepts: mail stream
  692.  * Returns: NIL
  693.  */
  694.  
  695. MAILSTREAM *mail_close (MAILSTREAM *stream)
  696. {
  697.   if (stream) {            /* make sure argument given */
  698.                 /* do the driver's close action */
  699.     if (stream->dtb) (*stream->dtb->close) (stream);
  700.     if (stream->mailbox) fs_give ((void **) &stream->mailbox);
  701.     stream->sequence++;        /* invalidate sequence */
  702.     if (stream->flagstring) fs_give ((void **) &stream->flagstring);
  703.     mail_free_cache (stream);    /* finally free the stream's storage */
  704.     if (!stream->use) fs_give ((void **) &stream);
  705.   }
  706.   return NIL;
  707. }
  708.  
  709. /* Mail make handle
  710.  * Accepts: mail stream
  711.  * Returns: handle
  712.  *
  713.  *  Handles provide a way to have multiple pointers to a stream yet allow the
  714.  * stream's owner to nuke it or recycle it.
  715.  */
  716.  
  717. MAILHANDLE *mail_makehandle (MAILSTREAM *stream)
  718. {
  719.   MAILHANDLE *handle = (MAILHANDLE *) fs_get (sizeof (MAILHANDLE));
  720.   handle->stream = stream;    /* copy stream */
  721.                 /* and its sequence */
  722.   handle->sequence = stream->sequence;
  723.   stream->use++;        /* let stream know another handle exists */
  724.   return handle;
  725. }
  726.  
  727.  
  728. /* Mail release handle
  729.  * Accepts: Mail handle
  730.  */
  731.  
  732. void mail_free_handle (MAILHANDLE **handle)
  733. {
  734.   MAILSTREAM *s;
  735.   if (*handle) {        /* only free if exists */
  736.                 /* resign stream, flush unreferenced zombies */
  737.     if ((!--(s = (*handle)->stream)->use) && !s->dtb) fs_give ((void **) &s);
  738.     fs_give ((void **) handle);    /* now flush the handle */
  739.   }
  740. }
  741.  
  742.  
  743. /* Mail get stream handle
  744.  * Accepts: Mail handle
  745.  * Returns: mail stream or NIL if stream gone
  746.  */
  747.  
  748. MAILSTREAM *mail_stream (MAILHANDLE *handle)
  749. {
  750.   MAILSTREAM *s = handle->stream;
  751.   return (s->dtb && (handle->sequence == s->sequence)) ? s : NIL;
  752. }
  753.  
  754. /* Mail fetch long cache element
  755.  * Accepts: mail stream
  756.  *        message # to fetch
  757.  * Returns: long cache element of this message
  758.  * Can also be used to create cache elements for new messages.
  759.  */
  760.  
  761. LONGCACHE *mail_lelt (MAILSTREAM *stream,long msgno)
  762. {
  763.   if (stream->scache) fatal ("Short cache in mail_lelt");
  764.                 /* be sure it the cache is large enough */
  765.   (*mailcache) (stream,msgno,CH_SIZE);
  766.   return (LONGCACHE *) (*mailcache) (stream,msgno,CH_MAKELELT);
  767. }
  768.  
  769.  
  770. /* Mail fetch cache element
  771.  * Accepts: mail stream
  772.  *        message # to fetch
  773.  * Returns: cache element of this message
  774.  * Can also be used to create cache elements for new messages.
  775.  */
  776.  
  777. MESSAGECACHE *mail_elt (MAILSTREAM *stream,long msgno)
  778. {
  779.   if (msgno < 1) fatal ("Bad msgno in mail_elt");
  780.                 /* be sure it the cache is large enough */
  781.   (*mailcache) (stream,msgno,CH_SIZE);
  782.   return (MESSAGECACHE *) (*mailcache) (stream,msgno,CH_MAKEELT);
  783. }
  784.  
  785. /* Mail fetch fast information
  786.  * Accepts: mail stream
  787.  *        sequence
  788.  *
  789.  * Generally, mail_fetchstructure is preferred
  790.  */
  791.  
  792. void mail_fetchfast (MAILSTREAM *stream,char *sequence)
  793. {
  794.                   /* do the driver's action */
  795.   if (stream->dtb) (*stream->dtb->fetchfast) (stream,sequence);
  796. }
  797.  
  798.  
  799. /* Mail fetch flags
  800.  * Accepts: mail stream
  801.  *        sequence
  802.  */
  803.  
  804. void mail_fetchflags (MAILSTREAM *stream,char *sequence)
  805. {
  806.                   /* do the driver's action */
  807.   if (stream->dtb) (*stream->dtb->fetchflags) (stream,sequence);
  808. }
  809.  
  810.  
  811. /* Mail fetch message structure
  812.  * Accepts: mail stream
  813.  *        message # to fetch
  814.  *        pointer to return body
  815.  * Returns: envelope of this message, body returned in body value
  816.  *
  817.  * Fetches the "fast" information as well
  818.  */
  819.  
  820. ENVELOPE *mail_fetchstructure (MAILSTREAM *stream,long msgno,BODY **body)
  821. {
  822.   if (msgno < 1 || msgno > stream->nmsgs)
  823.     fatal ("Bad msgno in mail_fetchstructure");
  824.                   /* do the driver's action */
  825.   return stream->dtb ? (*stream->dtb->fetchstructure) (stream,msgno,body) :NIL;
  826. }
  827.  
  828. /* Mail fetch message header
  829.  * Accepts: mail stream
  830.  *        message # to fetch
  831.  * Returns: message header in RFC822 format
  832.  */
  833.  
  834. char *mail_fetchheader (MAILSTREAM *stream,long msgno)
  835. {
  836.   if (msgno < 1 || msgno > stream->nmsgs)
  837.     fatal ("Bad msgno in mail_fetchheader");
  838.                   /* do the driver's action */
  839.   return stream->dtb ? (*stream->dtb->fetchheader) (stream,msgno) : "";
  840. }
  841.  
  842.  
  843. /* Mail fetch message text (body only)
  844.  * Accepts: mail stream
  845.  *        message # to fetch
  846.  * Returns: message text in RFC822 format
  847.  */
  848.  
  849. char *mail_fetchtext (MAILSTREAM *stream,long msgno)
  850. {
  851.   if (msgno < 1 || msgno > stream->nmsgs)
  852.     fatal ("Bad msgno in mail_fetchtext");
  853.                   /* do the driver's action */
  854.   return stream->dtb ? (*stream->dtb->fetchtext) (stream,msgno) : "";
  855. }
  856.  
  857.  
  858. /* Mail fetch message body part text
  859.  * Accepts: mail stream
  860.  *        message # to fetch
  861.  *        section specifier (#.#.#...#)
  862.  *        pointer to returned length
  863.  * Returns: pointer to section of message body
  864.  */
  865.  
  866. char *mail_fetchbody (MAILSTREAM *stream,long m,char *sec,unsigned long *len)
  867. {
  868.   if (m < 1 || m > stream->nmsgs) fatal ("Bad msgno in mail_fetchbody");
  869.                   /* do the driver's action */
  870.   return stream->dtb ? (*stream->dtb->fetchbody) (stream,m,sec,len) : "";
  871. }
  872.  
  873. /* Mail fetch From string for menu
  874.  * Accepts: destination string
  875.  *        mail stream
  876.  *        message # to fetch
  877.  *        desired string length
  878.  * Returns: string of requested length
  879.  */
  880.  
  881. void mail_fetchfrom (char *s,MAILSTREAM *stream,long msgno,long length)
  882. {
  883.   char *t;
  884.   char tmp[MAILTMPLEN];
  885.   ENVELOPE *env = mail_fetchstructure (stream,msgno,NIL);
  886.   ADDRESS *adr = env ? env->from : NIL;
  887.   memset (s,' ',length);    /* fill it with spaces */
  888.   s[length] = '\0';        /* tie off with null */
  889.                 /* get first from address from envelope */
  890.   while (adr && !adr->host) adr = adr->next;
  891.   if (adr) {            /* if a personal name exists use it */
  892.     if (!(t = adr->personal)) sprintf (t = tmp,"%s@%s",adr->mailbox,adr->host);
  893.     memcpy (s,t,min (length,(long) strlen (t)));
  894.   }
  895. }
  896.  
  897.  
  898. /* Mail fetch Subject string for menu
  899.  * Accepts: destination string
  900.  *        mail stream
  901.  *        message # to fetch
  902.  *        desired string length
  903.  * Returns: string of no more than requested length
  904.  */
  905.  
  906. void mail_fetchsubject (char *s,MAILSTREAM *stream,long msgno,long length)
  907. {
  908.   ENVELOPE *env = mail_fetchstructure (stream,msgno,NIL);
  909.   memset (s,'\0',length+1);
  910.                 /* copy subject from envelope */
  911.   if (env && env->subject) strncpy (s,env->subject,length);
  912.   else *s = ' ';        /* if no subject then just a space */
  913. }
  914.  
  915. /* Mail set flag
  916.  * Accepts: mail stream
  917.  *        sequence
  918.  *        flag(s)
  919.  */
  920.  
  921. void mail_setflag (MAILSTREAM *stream,char *sequence,char *flag)
  922. {
  923.                   /* do the driver's action */
  924.   if (stream->dtb) (*stream->dtb->setflag) (stream,sequence,flag);
  925. }
  926.  
  927.  
  928. /* Mail clear flag
  929.  * Accepts: mail stream
  930.  *        sequence
  931.  *        flag(s)
  932.  */
  933.  
  934. void mail_clearflag (MAILSTREAM *stream,char *sequence,char *flag)
  935. {
  936.                   /* do the driver's action */
  937.   if (stream->dtb) (*stream->dtb->clearflag) (stream,sequence,flag);
  938. }
  939.  
  940.  
  941. /* Mail search for messages
  942.  * Accepts: mail stream
  943.  *        search criteria
  944.  */
  945.  
  946. void mail_search (MAILSTREAM *stream,char *criteria)
  947. {
  948.   long i = 1;
  949.   while (i <= stream->nmsgs) mail_elt (stream,i++)->searched = NIL;
  950.                   /* do the driver's action */
  951.   if (stream->dtb) (*stream->dtb->search) (stream,criteria);
  952. }
  953.  
  954.  
  955. /* Mail ping mailbox
  956.  * Accepts: mail stream
  957.  * Returns: stream if still open else NIL
  958.  */
  959.  
  960. long mail_ping (MAILSTREAM *stream)
  961. {
  962.                   /* do the driver's action */
  963.   return stream->dtb ? (*stream->dtb->ping) (stream) : NIL;
  964. }
  965.  
  966. /* Mail check mailbox
  967.  * Accepts: mail stream
  968.  */
  969.  
  970. void mail_check (MAILSTREAM *stream)
  971. {
  972.                   /* do the driver's action */
  973.   if (stream->dtb) (*stream->dtb->check) (stream);
  974. }
  975.  
  976.  
  977. /* Mail expunge mailbox
  978.  * Accepts: mail stream
  979.  */
  980.  
  981. void mail_expunge (MAILSTREAM *stream)
  982. {
  983.                   /* do the driver's action */
  984.   if (stream->dtb) (*stream->dtb->expunge) (stream);
  985. }
  986.  
  987. /* Mail copy message(s)
  988.  * Accepts: mail stream
  989.  *        sequence
  990.  *        destination mailbox
  991.  */
  992.  
  993. long mail_copy (MAILSTREAM *stream,char *sequence,char *mailbox)
  994. {
  995.                   /* do the driver's action */
  996.   return stream->dtb ? (*stream->dtb->copy) (stream,sequence,mailbox) : NIL;
  997. }
  998.  
  999.  
  1000. /* Mail move message(s)
  1001.  * Accepts: mail stream
  1002.  *        sequence
  1003.  *        destination mailbox
  1004.  */
  1005.  
  1006. long mail_move (MAILSTREAM *stream,char *sequence,char *mailbox)
  1007. {
  1008.                   /* do the driver's action */
  1009.   return stream->dtb ? (*stream->dtb->move) (stream,sequence,mailbox) : NIL;
  1010. }
  1011.  
  1012.  
  1013. /* Mail append message string
  1014.  * Accepts: mail stream
  1015.  *        destination mailbox
  1016.  *        initial flags
  1017.  *        message internal date
  1018.  *        stringstruct of message to append
  1019.  * Returns: T on success, NIL on failure
  1020.  */
  1021.  
  1022. long mail_append (MAILSTREAM *stream,char *mailbox,STRING *message)
  1023. {
  1024.                 /* compatibility jacket */
  1025.   return mail_append_full (stream,mailbox,NIL,NIL,message);
  1026. }
  1027.  
  1028.  
  1029. long mail_append_full (MAILSTREAM *stream,char *mailbox,char *flags,char *date,
  1030.                STRING *message)
  1031. {
  1032.   DRIVER *factory = mail_valid (stream,mailbox,NIL);
  1033.   if (!factory) {        /* got a driver to use? */
  1034.     if (!stream &&        /* ask default for TRYCREATE if no stream */
  1035.     (*default_proto ()->dtb->append) (stream,mailbox,flags,date,message)) {
  1036.                 /* timing race?? */
  1037.       mm_notify (stream,"Append validity confusion",WARN);
  1038.       return T;
  1039.     }
  1040.                 /* now generate error message */
  1041.     mail_valid (stream,mailbox,"append to mailbox");
  1042.     return NIL;            /* return failure */
  1043.   }
  1044.                 /* do the driver's action */
  1045.   return (factory->append) (stream,mailbox,flags,date,message);
  1046. }
  1047.  
  1048. /* Mail garbage collect stream
  1049.  * Accepts: mail stream
  1050.  *        garbage collection flags
  1051.  */
  1052.  
  1053. void mail_gc (MAILSTREAM *stream,long gcflags)
  1054. {
  1055.   unsigned long i = 1;
  1056.   LONGCACHE *lelt;
  1057.                   /* do the driver's action first */
  1058.   if (stream->dtb) (*stream->dtb->gc) (stream,gcflags);
  1059.   if (gcflags & GC_ENV) {    /* garbage collect envelopes? */
  1060.                 /* yes, free long cache if in use */
  1061.     if (!stream->scache) while (i <= stream->nmsgs)
  1062.       if (lelt = (LONGCACHE *) (*mailcache) (stream,i++,CH_LELT)) {
  1063.     mail_free_envelope (&lelt->env);
  1064.     mail_free_body (&lelt->body);
  1065.       }
  1066.     stream->msgno = 0;        /* free this cruft too */
  1067.     mail_free_envelope (&stream->env);
  1068.     mail_free_body (&stream->body);
  1069.   }
  1070.                 /* free text if any */
  1071.   if ((gcflags & GC_TEXTS) && (stream->text)) fs_give ((void **)&stream->text);
  1072. }
  1073.  
  1074. /* Mail output date from elt fields
  1075.  * Accepts: character string to write into
  1076.  *        elt to get data data from
  1077.  * Returns: the character string
  1078.  */
  1079.  
  1080. const char *days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
  1081.  
  1082. const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
  1083.             "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
  1084.  
  1085. char *mail_date (char *string,MESSAGECACHE *elt)
  1086. {
  1087.   const char *s = (elt->month && elt->month < 13) ?
  1088.     months[elt->month - 1] : (const char *) "???";
  1089.   sprintf (string,"%2d-%s-%d %02d:%02d:%02d %c%02d%02d",
  1090.        elt->day,s,elt->year + BASEYEAR,
  1091.        elt->hours,elt->minutes,elt->seconds,
  1092.        elt->zoccident ? '-' : '+',elt->zhours,elt->zminutes);
  1093.   return string;
  1094. }
  1095.  
  1096.  
  1097. /* Mail output cdate format date from elt fields
  1098.  * Accepts: character string to write into
  1099.  *        elt to get data data from
  1100.  * Returns: the character string
  1101.  */
  1102.  
  1103. char *mail_cdate (char *string,MESSAGECACHE *elt)
  1104. {
  1105.   const char *s = (elt->month && elt->month < 13) ?
  1106.     months[elt->month - 1] : (const char *) "???";
  1107.   int m = elt->month;
  1108.   int y = elt->year + BASEYEAR;
  1109.   if (elt->month <= 2) {    /* if before March, */
  1110.     m = elt->month + 9;        /* January = month 10 of previous year */
  1111.     y--;
  1112.   }
  1113.   else m = elt->month - 3;    /* March is month 0 */
  1114.   sprintf (string,"%s %s %2d %02d:%02d:%02d %4d\n",
  1115.        days[(int)(elt->day+2+((7+31*m)/12)+y+(y/4)+(y/400)-(y/100)) % 7],s,
  1116.        elt->day,elt->hours,elt->minutes,elt->seconds,elt->year + BASEYEAR);
  1117.   return string;
  1118. }
  1119.  
  1120. /* Mail parse date into elt fields
  1121.  * Accepts: elt to write into
  1122.  *        date string to parse
  1123.  * Returns: T if parse successful, else NIL 
  1124.  * This routine parses dates as follows:
  1125.  * . leading three alphas followed by comma and space are ignored
  1126.  * . date accepted in format: mm/dd/yy, mm/dd/yyyy, dd-mmm-yy, dd-mmm-yyyy,
  1127.  *    dd mmm yy, dd mmm yyyy
  1128.  * . space or end of string required
  1129.  * . time accepted in format hh:mm:ss or hh:mm
  1130.  * . end of string accepted
  1131.  * . timezone accepted: hyphen followed by symbolic timezone, or space
  1132.  *    followed by signed numeric timezone or symbolic timezone
  1133.  * Examples of normal input:
  1134.  * . IMAP date-only (SEARCH): dd-mmm-yy, dd-mmm-yyyy, mm/dd/yy, mm/dd/yyyy
  1135.  * . IMAP date-time (INTERNALDATE):
  1136.  *    dd-mmm-yy hh:mm:ss-zzz
  1137.  *    dd-mmm-yyyy hh:mm:ss +zzzz
  1138.  * . RFC-822:
  1139.  *    www, dd mmm yy hh:mm:ss zzz
  1140.  *    www, dd mmm yyyy hh:mm:ss +zzzz
  1141.  */
  1142.  
  1143. long mail_parse_date (MESSAGECACHE *elt,char *s)
  1144. {
  1145.   long d,m,y;
  1146.   int ms;
  1147.   struct tm *t;
  1148.   time_t tn;
  1149.   char tmp[MAILTMPLEN];
  1150.                 /* make a writeable uppercase copy */
  1151.   if (s && *s && (strlen (s) < MAILTMPLEN)) s = ucase (strcpy (tmp,s));
  1152.   else return NIL;
  1153.                 /* skip over possible day of week */
  1154.   if (isalpha (*s) && isalpha (s[1]) && isalpha (s[2]) && (s[3] == ',') &&
  1155.       (s[4] == ' ')) s += 5;
  1156.   while (*s == ' ') s++;    /* parse first number (probable month) */
  1157.   if (!(m = strtol ((const char *) s,&s,10))) return NIL;
  1158.  
  1159.   switch (*s) {            /* different parse based on delimiter */
  1160.   case '/':            /* mm/dd/yy format */
  1161.     if (!((d = strtol ((const char *) ++s,&s,10)) && *s == '/' &&
  1162.       (y = strtol ((const char *) ++s,&s,10)) && *s == '\0')) return NIL;
  1163.     break;
  1164.   case ' ':            /* dd mmm yy format */
  1165.     while (s[1] == ' ') s++;    /* slurp extra whitespace */
  1166.   case '-':            /* dd-mmm-yy format */
  1167.     d = m;            /* so the number we got is a day */
  1168.                 /* make sure string long enough! */
  1169.     if (strlen (s) < 5) return NIL;
  1170.     /* Some compilers don't allow `<<' and/or longs in case statements. */
  1171.                 /* slurp up the month string */
  1172.     ms = ((s[1] - 'A') * 1024) + ((s[2] - 'A') * 32) + (s[3] - 'A');
  1173.     switch (ms) {        /* determine the month */
  1174.     case (('J'-'A') * 1024) + (('A'-'A') * 32) + ('N'-'A'): m = 1; break;
  1175.     case (('F'-'A') * 1024) + (('E'-'A') * 32) + ('B'-'A'): m = 2; break;
  1176.     case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('R'-'A'): m = 3; break;
  1177.     case (('A'-'A') * 1024) + (('P'-'A') * 32) + ('R'-'A'): m = 4; break;
  1178.     case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('Y'-'A'): m = 5; break;
  1179.     case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('N'-'A'): m = 6; break;
  1180.     case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('L'-'A'): m = 7; break;
  1181.     case (('A'-'A') * 1024) + (('U'-'A') * 32) + ('G'-'A'): m = 8; break;
  1182.     case (('S'-'A') * 1024) + (('E'-'A') * 32) + ('P'-'A'): m = 9; break;
  1183.     case (('O'-'A') * 1024) + (('C'-'A') * 32) + ('T'-'A'): m = 10; break;
  1184.     case (('N'-'A') * 1024) + (('O'-'A') * 32) + ('V'-'A'): m = 11; break;
  1185.     case (('D'-'A') * 1024) + (('E'-'A') * 32) + ('C'-'A'): m = 12; break;
  1186.     default: return NIL;    /* unknown month */
  1187.     }
  1188.     if ((s[4] == *s) &&    (y = strtol ((const char *) s+5,&s,10)) &&
  1189.     (*s == '\0' || *s == ' ')) break;
  1190.   default: return NIL;        /* unknown date format */
  1191.   }
  1192.                 /* minimal validity check of date */
  1193.   if (d < 1 || d > 31 || m < 1 || m > 12 || y < 0) return NIL; 
  1194.                 /* Tenex/ARPAnet began in 1969 */
  1195.   if (y < 100) y += (y >= (BASEYEAR - 1900)) ? 1900 : 2000;
  1196.                 /* set values in elt */
  1197.   elt->day = d; elt->month = m; elt->year = y - BASEYEAR;
  1198.  
  1199.   if (*s) {            /* time specification present? */
  1200.                 /* parse time */
  1201.     d = strtol ((const char *) s,&s,10);
  1202.     if (*s != ':') return NIL;
  1203.     m = strtol ((const char *) ++s,&s,10);
  1204.     y = (*s == ':') ? strtol ((const char *) ++s,&s,10) : 0;
  1205.                 /* validity check time */
  1206.     if (d < 0 || d > 23 || m < 0 || m > 59 || y < 0 || y > 59) return NIL; 
  1207.                 /* set values in elt */
  1208.     elt->hours = d; elt->minutes = m; elt->seconds = y;
  1209.     switch (*s) {        /* time zone specifier? */
  1210.     case ' ':            /* numeric time zone */
  1211.       while (s[1] == ' ') s++;    /* slurp extra whitespace */
  1212.       if (!isalpha (s[1])) {    /* treat as '-' case if alphabetic */
  1213.                 /* test for sign character */
  1214.     if ((elt->zoccident = (*++s == '-')) || (*s == '+')) s++;
  1215.                 /* validate proper timezone */
  1216.     if (isdigit(*s) && isdigit(s[1]) && isdigit(s[2]) && isdigit(s[3])) {
  1217.       elt->zhours = (*s - '0') * 10 + (s[1] - '0');
  1218.       elt->zminutes = (s[2] - '0') * 10 + (s[3] - '0');
  1219.     }
  1220.     return T;        /* all done! */
  1221.       }
  1222.                 /* falls through */
  1223.  
  1224.     case '-':            /* symbolic time zone */
  1225.       if (!(ms = *++s)) ms = 'Z';
  1226.       else if (*++s) {        /* multi-character? */
  1227.     ms -= 'A'; ms *= 1024;    /* yes, make compressed three-byte form */
  1228.     ms += ((*s++ - 'A') * 32);
  1229.     if (*s) ms += *s++ - 'A';
  1230.     if (*s) ms = '\0';    /* more than three characters */
  1231.       }
  1232.       /* This is not intended to be a comprehensive list of all possible
  1233.        * timezone strings.  Such a list would be impractical.  Rather, this
  1234.        * listing is intended to incorporate all military, north American, and
  1235.        * a few special cases such as Japan and the major European zone names,
  1236.        * such as what might be expected to be found in a Tenex format mailbox
  1237.        * and spewed from an IMAP server.  The trend is to migrate to numeric
  1238.        * timezones which lack the flavor but also the ambiguity of the names.
  1239.        */
  1240.       switch (ms) {        /* determine the timezone */
  1241.     /* oriental (from Greenwich) timezones */
  1242.                 /* Middle Europe */
  1243.       case (('M'-'A')*1024)+(('E'-'A')*32)+'T'-'A':
  1244.       case 'A': elt->zhours = 1; break;
  1245.                 /* Eastern Europe */
  1246.       case (('E'-'A')*1024)+(('E'-'A')*32)+'T'-'A':
  1247.       case 'B': elt->zhours = 2; break;
  1248.       case 'C': elt->zhours = 3; break;
  1249.       case 'D': elt->zhours = 4; break;
  1250.       case 'E': elt->zhours = 5; break;
  1251.       case 'F': elt->zhours = 6; break;
  1252.       case 'G': elt->zhours = 7; break;
  1253.       case 'H': elt->zhours = 8; break;
  1254.                 /* Japan */
  1255.       case (('J'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1256.       case 'I': elt->zhours = 9; break;
  1257.       case 'K': elt->zhours = 10; break;
  1258.       case 'L': elt->zhours = 11; break;
  1259.       case 'M': elt->zhours = 12; break;
  1260.  
  1261.     /* occidental (from Greenwich) timezones */
  1262.       case 'N': elt->zoccident = 1; elt->zhours = 1; break;
  1263.       case 'O': elt->zoccident = 1; elt->zhours = 2; break;
  1264.       case (('A'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1265.       case 'P': elt->zoccident = 1; elt->zhours = 3; break;
  1266.                 /* Atlantic */
  1267.       case (('A'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1268.       case (('E'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1269.       case 'Q': elt->zoccident = 1; elt->zhours = 4; break;
  1270.                 /* Eastern */
  1271.       case (('E'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1272.       case (('C'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1273.       case 'R': elt->zoccident = 1; elt->zhours = 5; break;
  1274.                 /* Central */
  1275.       case (('C'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1276.       case (('M'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1277.       case 'S': elt->zoccident = 1; elt->zhours = 6; break;
  1278.                 /* Mountain */
  1279.       case (('M'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1280.       case (('P'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1281.       case 'T': elt->zoccident = 1; elt->zhours = 7; break;
  1282.                 /* Pacific */
  1283.       case (('P'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1284.       case (('Y'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1285.       case 'U': elt->zoccident = 1; elt->zhours = 8; break;
  1286.                 /* Yukon */
  1287.       case (('Y'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1288.       case (('H'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1289.       case 'V': elt->zoccident = 1; elt->zhours = 9; break;
  1290.                 /* Hawaii */
  1291.       case (('H'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1292.       case (('B'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1293.       case 'W': elt->zoccident = 1; elt->zhours = 10; break;
  1294.                 /* Bering */
  1295.       case (('B'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1296.       case 'X': elt->zoccident = 1; elt->zhours = 11; break;
  1297.       case 'Y': elt->zoccident = 1; elt->zhours = 12; break;
  1298.                 /* Universal */
  1299.       case (('U'-'A')*1024)+(('T'-'A')*32):
  1300.       case (('G'-'A')*1024)+(('M'-'A')*32)+'T'-'A':
  1301.       case 'Z': elt->zhours = 0; break;
  1302.  
  1303.       default:            /* assume local otherwise */
  1304.     tn = time (0);        /* time now... */
  1305.     t = localtime (&tn);    /* get local minutes since midnight */
  1306.     m = t->tm_hour * 60 + t->tm_min;
  1307.     ms = t->tm_yday;    /* note Julian day */
  1308.     t = gmtime (&tn);    /* minus UTC minutes since midnight */
  1309.     m -= t->tm_hour * 60 + t->tm_min;
  1310.     /* ms can be one of:
  1311.      *  36x  local time is December 31, UTC is January 1, offset -24 hours
  1312.      *    1  local time is 1 day ahead of UTC, offset +24 hours
  1313.      *    0  local time is same day as UTC, no offset
  1314.      *   -1  local time is 1 day behind UTC, offset -24 hours
  1315.      * -36x  local time is January 1, UTC is December 31, offset +24 hours
  1316.      */
  1317.     if (ms -= t->tm_yday)    /* correct offset if different Julian day */
  1318.       m += ((ms < 0) == (abs (ms) == 1)) ? -24*60 : 24*60;
  1319.     if (m < 0) {        /* occidental? */
  1320.       m = abs (m);        /* yup, make positive number */
  1321.       elt->zoccident = 1;    /* and note west of UTC */
  1322.     }
  1323.     elt->zhours = m / 60;    /* now break into hours and minutes */
  1324.     elt->zminutes = m % 60;
  1325.     break;
  1326.       }
  1327.       elt->zminutes = 0;    /* never a fractional hour */
  1328.       break;
  1329.     case '\0':            /* no time zone */
  1330.     default:            /* bogus time zone */
  1331.       break;            /* ignore both cases */
  1332.     }
  1333.   }
  1334.   else {            /* make sure all time fields zero */
  1335.     elt->hours = elt->minutes = elt->seconds = elt->zhours = elt->zminutes =
  1336.       elt->zoccident = 0;
  1337.   }
  1338.   return T;
  1339. }
  1340.  
  1341. /* Mail messages have been searched out
  1342.  * Accepts: mail stream
  1343.  *        message number
  1344.  *
  1345.  * Calls external "mm_searched" function to notify main program
  1346.  */
  1347.  
  1348. void mail_searched (MAILSTREAM *stream,long msgno)
  1349. {
  1350.                 /* mark as searched */
  1351.   mail_elt (stream,msgno)->searched = T;
  1352.   mm_searched (stream,msgno);    /* notify main program */
  1353. }
  1354.  
  1355.  
  1356. /* Mail n messages exist
  1357.  * Accepts: mail stream
  1358.  *        number of messages
  1359.  *
  1360.  * Calls external "mm_exists" function that notifies main program prior
  1361.  * to updating the stream
  1362.  */
  1363.  
  1364. void mail_exists (MAILSTREAM *stream,long nmsgs)
  1365. {
  1366.                 /* make sure cache is large enough */
  1367.   (*mailcache) (stream,nmsgs,CH_SIZE);
  1368.                 /* notify main program of change */
  1369.   if (!stream->silent) mm_exists (stream,nmsgs);
  1370.   stream->nmsgs = nmsgs;    /* update stream status */
  1371. }
  1372.  
  1373. /* Mail n messages are recent
  1374.  * Accepts: mail stream
  1375.  *        number of recent messages
  1376.  */
  1377.  
  1378. void mail_recent (MAILSTREAM *stream,long recent)
  1379. {
  1380.   stream->recent = recent;    /* update stream status */
  1381. }
  1382.  
  1383.  
  1384. /* Mail message n is expunged
  1385.  * Accepts: mail stream
  1386.  *        message #
  1387.  *
  1388.  * Calls external "mm_expunged" function that notifies main program prior
  1389.  * to updating the stream
  1390.  */
  1391.  
  1392. void mail_expunged (MAILSTREAM *stream,long msgno)
  1393. {
  1394.   long i = msgno - 1;
  1395.   MESSAGECACHE *elt = (MESSAGECACHE *) (*mailcache) (stream,msgno,CH_ELT);
  1396.   if (elt) {            /* if an element is there */
  1397.     elt->msgno = 0;        /* invalidate its message number and free */
  1398.     (*mailcache) (stream,msgno,CH_FREE);
  1399.   }
  1400.                 /* expunge the slot */
  1401.   (*mailcache) (stream,msgno,CH_EXPUNGE);
  1402.   --stream->nmsgs;        /* update stream status */
  1403.   stream->msgno = 0;        /* nuke the short cache too */
  1404.   mail_free_envelope (&stream->env);
  1405.   mail_free_body (&stream->body);
  1406.                 /* notify main program of change */
  1407.   if (!stream->silent) mm_expunged (stream,msgno);
  1408. }
  1409.  
  1410. /* mail stream status routines */
  1411.  
  1412.  
  1413. /* Mail lock stream
  1414.  * Accepts: mail stream
  1415.  */
  1416.  
  1417. void mail_lock (MAILSTREAM *stream)
  1418. {
  1419.   if (stream->lock) fatal ("Lock when already locked");
  1420.   else stream->lock = T;    /* lock stream */
  1421. }
  1422.  
  1423.  
  1424. /* Mail unlock stream
  1425.  * Accepts: mail stream
  1426.  */
  1427.  
  1428. void mail_unlock (MAILSTREAM *stream)
  1429. {
  1430.   if (!stream->lock) fatal ("Unlock when not locked");
  1431.   else stream->lock = NIL;    /* unlock stream */
  1432. }
  1433.  
  1434.  
  1435. /* Mail turn on debugging telemetry
  1436.  * Accepts: mail stream
  1437.  */
  1438.  
  1439. void mail_debug (MAILSTREAM *stream)
  1440. {
  1441.   stream->debug = T;        /* turn on debugging telemetry */
  1442. }
  1443.  
  1444.  
  1445. /* Mail turn off debugging telemetry
  1446.  * Accepts: mail stream
  1447.  */
  1448.  
  1449. void mail_nodebug (MAILSTREAM *stream)
  1450. {
  1451.   stream->debug = NIL;        /* turn off debugging telemetry */
  1452. }
  1453.  
  1454. /* Mail parse sequence
  1455.  * Accepts: mail stream
  1456.  *        sequence to parse
  1457.  * Returns: T if parse successful, else NIL
  1458.  */
  1459.  
  1460. long mail_sequence (MAILSTREAM *stream,char *sequence)
  1461. {
  1462.   long i,j,x;
  1463.   for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->sequence = NIL;
  1464.   while (*sequence) {        /* while there is something to parse */
  1465.                 /* parse and validate message number */
  1466.     if (((i = (int) strtol ((const char *) sequence,&sequence,10)) < 1) ||
  1467.     (i > stream->nmsgs)) {
  1468.       mm_log ("Sequence invalid",ERROR);
  1469.       return NIL;
  1470.     }
  1471.     switch (*sequence) {    /* see what the delimiter is */
  1472.     case ':':            /* sequence range */
  1473.                 /* parse end of range */
  1474.       if (((j = (int) strtol ((const char *) ++sequence,&sequence,10)) < 1) ||
  1475.       (j > stream->nmsgs) || (*sequence && *sequence++ != ',')) {
  1476.     mm_log ("Sequence range invalid",ERROR);
  1477.     return NIL;
  1478.       }
  1479.       if (i > j) {        /* swap the range if backwards */
  1480.     x = i; i = j; j = x;
  1481.       }
  1482.                 /* mark each item in the sequence */
  1483.       while (i <= j) mail_elt (stream,j--)->sequence = T;
  1484.       break;
  1485.     case ',':            /* single message */
  1486.       ++sequence;        /* skip the delimiter, fall into end case */
  1487.     case '\0':            /* end of sequence, mark this message */
  1488.       mail_elt (stream,i)->sequence = T;
  1489.       break;
  1490.     default:            /* anything else is a syntax error! */
  1491.       mm_log ("Syntax error in sequence",ERROR);
  1492.       return NIL;
  1493.     }
  1494.   }
  1495.   return T;            /* successfully parsed sequence */
  1496. }
  1497.  
  1498. /* Mail data structure instantiation routines */
  1499.  
  1500.  
  1501. /* Mail instantiate envelope
  1502.  * Returns: new envelope
  1503.  */
  1504.  
  1505. ENVELOPE *mail_newenvelope ()
  1506. {
  1507.   ENVELOPE *env = (ENVELOPE *) fs_get (sizeof (ENVELOPE));
  1508.   env->remail = NIL;        /* initialize all fields */
  1509.   env->return_path = NIL;
  1510.   env->date = NIL;
  1511.   env->subject = NIL;
  1512.   env->from = env->sender = env->reply_to = env->to = env->cc = env->bcc = NIL;
  1513.   env->in_reply_to = env->message_id = env->newsgroups = env->followup_to =
  1514.     env->references = NIL;
  1515.   return env;
  1516. }
  1517.  
  1518.  
  1519. /* Mail instantiate address
  1520.  * Returns: new address
  1521.  */
  1522.  
  1523. ADDRESS *mail_newaddr ()
  1524. {
  1525.   ADDRESS *adr = (ADDRESS *) fs_get (sizeof (ADDRESS));
  1526.                 /* initialize all fields */
  1527.   adr->personal = adr->adl = adr->mailbox = adr->host = adr->error = NIL;
  1528.   adr->next = NIL;
  1529.   return adr;
  1530. }
  1531.  
  1532.  
  1533. /* Mail instantiate body
  1534.  * Returns: new body
  1535.  */
  1536.  
  1537. BODY *mail_newbody ()
  1538. {
  1539.   return mail_initbody ((BODY *) fs_get (sizeof (BODY)));
  1540. }
  1541.  
  1542. /* Mail initialize body
  1543.  * Accepts: body
  1544.  * Returns: body
  1545.  */
  1546.  
  1547. BODY *mail_initbody (BODY *body)
  1548. {
  1549.   body->type = TYPETEXT;    /* content type */
  1550.   body->encoding = ENC7BIT;    /* content encoding */
  1551.   body->subtype = body->id = body->description = NIL;
  1552.   body->parameter = NIL;
  1553.   body->contents.text = NIL;    /* no contents yet */
  1554.   body->contents.binary = NIL;
  1555.   body->contents.part = NIL;
  1556.   body->contents.msg.env = NIL;
  1557.   body->contents.msg.body = NIL;
  1558.   body->contents.msg.text = NIL;
  1559.   body->size.lines = body->size.bytes = body->size.ibytes = 0;
  1560.   body->md5 = NIL;
  1561.   return body;
  1562. }
  1563.  
  1564.  
  1565. /* Mail instantiate body parameter
  1566.  * Returns: new body part
  1567.  */
  1568.  
  1569. PARAMETER *mail_newbody_parameter ()
  1570. {
  1571.   PARAMETER *parameter = (PARAMETER *) fs_get (sizeof (PARAMETER));
  1572.   parameter->attribute = parameter->value = NIL;
  1573.   parameter->next = NIL;    /* no next yet */
  1574.   return parameter;
  1575. }
  1576.  
  1577.  
  1578. /* Mail instantiate body part
  1579.  * Returns: new body part
  1580.  */
  1581.  
  1582. PART *mail_newbody_part ()
  1583. {
  1584.   PART *part = (PART *) fs_get (sizeof (PART));
  1585.   mail_initbody (&part->body);    /* initialize the body */
  1586.   part->offset = 0;        /* no offset yet */
  1587.   part->next = NIL;        /* no next yet */
  1588.   return part;
  1589. }
  1590.  
  1591. /* Mail garbage collection routines */
  1592.  
  1593.  
  1594. /* Mail garbage collect body
  1595.  * Accepts: pointer to body pointer
  1596.  */
  1597.  
  1598. void mail_free_body (BODY **body)
  1599. {
  1600.   if (*body) {            /* only free if exists */
  1601.     mail_free_body_data (*body);/* free its data */
  1602.     fs_give ((void **) body);    /* return body to free storage */
  1603.   }
  1604. }
  1605.  
  1606.  
  1607. /* Mail garbage collect body data
  1608.  * Accepts: body pointer
  1609.  */
  1610.  
  1611. void mail_free_body_data (BODY *body)
  1612. {
  1613.   if (body->subtype) fs_give ((void **) &body->subtype);
  1614.   mail_free_body_parameter (&body->parameter);
  1615.   if (body->id) fs_give ((void **) &body->id);
  1616.   if (body->description) fs_give ((void **) &body->description);
  1617.   if (body->md5) fs_give ((void **) &body->md5);
  1618.   switch (body->type) {        /* free contents */
  1619.   case TYPETEXT:        /* unformatted text */
  1620.     if (body->contents.text) fs_give ((void **) &body->contents.text);
  1621.     break;
  1622.   case TYPEMULTIPART:        /* multiple part */
  1623.     mail_free_body_part (&body->contents.part);
  1624.     break;
  1625.   case TYPEMESSAGE:        /* encapsulated message */
  1626.     mail_free_envelope (&body->contents.msg.env);
  1627.     mail_free_body (&body->contents.msg.body);
  1628.     if (body->contents.msg.text)
  1629.       fs_give ((void **) &body->contents.msg.text);
  1630.     break;
  1631.   case TYPEAPPLICATION:        /* application data */
  1632.   case TYPEAUDIO:        /* audio */
  1633.   case TYPEIMAGE:        /* static image */
  1634.   case TYPEVIDEO:        /* video */
  1635.     if (body->contents.binary) fs_give (&body->contents.binary);
  1636.     break;
  1637.   default:
  1638.     break;
  1639.   }
  1640. }
  1641.  
  1642. /* Mail garbage collect body parameter
  1643.  * Accepts: pointer to body parameter pointer
  1644.  */
  1645.  
  1646. void mail_free_body_parameter (PARAMETER **parameter)
  1647. {
  1648.   if (*parameter) {        /* only free if exists */
  1649.     if ((*parameter)->attribute) fs_give ((void **) &(*parameter)->attribute);
  1650.     if ((*parameter)->value) fs_give ((void **) &(*parameter)->value);
  1651.                 /* run down the list as necessary */
  1652.     mail_free_body_parameter (&(*parameter)->next);
  1653.                 /* return body part to free storage */
  1654.     fs_give ((void **) parameter);
  1655.   }
  1656. }
  1657.  
  1658.  
  1659. /* Mail garbage collect body part
  1660.  * Accepts: pointer to body part pointer
  1661.  */
  1662.  
  1663. void mail_free_body_part (PART **part)
  1664. {
  1665.   if (*part) {            /* only free if exists */
  1666.     mail_free_body_data (&(*part)->body);
  1667.                 /* run down the list as necessary */
  1668.     mail_free_body_part (&(*part)->next);
  1669.     fs_give ((void **) part);    /* return body part to free storage */
  1670.   }
  1671. }
  1672.  
  1673. /* Mail garbage collect message cache
  1674.  * Accepts: mail stream
  1675.  *
  1676.  * The message cache is set to NIL when this function finishes.
  1677.  */
  1678.  
  1679. void mail_free_cache (MAILSTREAM *stream)
  1680. {
  1681.                 /* flush the cache */
  1682.   (*mailcache) (stream,(long) 0,CH_INIT);
  1683.   stream->msgno = 0;        /* free this cruft too */
  1684.   mail_free_envelope (&stream->env);
  1685.   mail_free_body (&stream->body);
  1686.   if (stream->text) fs_give ((void **) &stream->text);
  1687. }
  1688.  
  1689.  
  1690. /* Mail garbage collect cache element
  1691.  * Accepts: pointer to cache element pointer
  1692.  */
  1693.  
  1694. void mail_free_elt (MESSAGECACHE **elt)
  1695. {
  1696.                 /* only free if exists and no sharers */
  1697.   if (*elt && !--(*elt)->lockcount) fs_give ((void **) elt);
  1698.   else *elt = NIL;        /* else simply drop pointer */
  1699. }
  1700.  
  1701.  
  1702. /* Mail garbage collect long cache element
  1703.  * Accepts: pointer to long cache element pointer
  1704.  */
  1705.  
  1706. void mail_free_lelt (LONGCACHE **lelt)
  1707. {
  1708.                 /* only free if exists and no sharers */
  1709.   if (*lelt && !--(*lelt)->elt.lockcount) {
  1710.     mail_free_envelope (&(*lelt)->env);
  1711.     mail_free_body (&(*lelt)->body);
  1712.     fs_give ((void **) lelt);    /* return cache element to free storage */
  1713.   }
  1714.   else *lelt = NIL;        /* else simply drop pointer */
  1715. }
  1716.  
  1717. /* Mail garbage collect envelope
  1718.  * Accepts: pointer to envelope pointer
  1719.  */
  1720.  
  1721. void mail_free_envelope (ENVELOPE **env)
  1722. {
  1723.   if (*env) {            /* only free if exists */
  1724.     if ((*env)->remail) fs_give ((void **) &(*env)->remail);
  1725.     mail_free_address (&(*env)->return_path);
  1726.     if ((*env)->date) fs_give ((void **) &(*env)->date);
  1727.     mail_free_address (&(*env)->from);
  1728.     mail_free_address (&(*env)->sender);
  1729.     mail_free_address (&(*env)->reply_to);
  1730.     if ((*env)->subject) fs_give ((void **) &(*env)->subject);
  1731.     mail_free_address (&(*env)->to);
  1732.     mail_free_address (&(*env)->cc);
  1733.     mail_free_address (&(*env)->bcc);
  1734.     if ((*env)->in_reply_to) fs_give ((void **) &(*env)->in_reply_to);
  1735.     if ((*env)->message_id) fs_give ((void **) &(*env)->message_id);
  1736.     if ((*env)->newsgroups) fs_give ((void **) &(*env)->newsgroups);
  1737.     if ((*env)->followup_to) fs_give ((void **) &(*env)->followup_to);
  1738.     if ((*env)->references) fs_give ((void **) &(*env)->references);
  1739.     fs_give ((void **) env);    /* return envelope to free storage */
  1740.   }
  1741. }
  1742.  
  1743.  
  1744. /* Mail garbage collect address
  1745.  * Accepts: pointer to address pointer
  1746.  */
  1747.  
  1748. void mail_free_address (ADDRESS **address)
  1749. {
  1750.   if (*address) {        /* only free if exists */
  1751.     if ((*address)->personal) fs_give ((void **) &(*address)->personal);
  1752.     if ((*address)->adl) fs_give ((void **) &(*address)->adl);
  1753.     if ((*address)->mailbox) fs_give ((void **) &(*address)->mailbox);
  1754.     if ((*address)->host) fs_give ((void **) &(*address)->host);
  1755.     if ((*address)->error) fs_give ((void **) &(*address)->error);
  1756.     mail_free_address (&(*address)->next);
  1757.     fs_give ((void **) address);/* return address to free storage */
  1758.   }
  1759. }
  1760.